package com.yvan.serverless.script.internal;

import com.yvan.Conv;
import com.yvan.serverless.utils.ScriptUtils;
import lombok.SneakyThrows;
import oracle.sql.TIMESTAMP;
import org.apache.commons.lang3.time.DateUtils;
import org.graalvm.polyglot.Value;
import org.joda.time.DateTime;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.sql.Timestamp;
import java.time.*;
import java.util.*;

/**
 * 作者：lizw <br/>
 * 创建时间：2020/07/31 10:43 <br/>
 *
 * @param <T> script引擎对象类型
 */
public abstract class Interop<T> {
    private static final String[] Date_Patterns = {
            "yyyy-MM-dd HH:mm:ss",
            "yyyy-MM-dd HH:mm:ss.SSS",
            "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'",
            "EEE MMM dd HH:mm:ss zzz yyyy",
            "yyyy-MM-dd", "HH:mm:ss",
    };

    protected Interop() {
    }

    public String replaceAll(String target, String search, String replace) {
        return Conv.asString(target).replace(search, replace);
    }

    public Class<?> getClass(String className) {
        Class<?> clazz = null;
        try {
            clazz = Class.forName(className);
        } catch (ClassNotFoundException ignored) {
        }
        return clazz;
    }

    public Object newJObject(String className) {
        try {
            Class<?> clazz = Class.forName(className);
            return clazz.getDeclaredConstructor().newInstance();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public short asJShort(Value value) {
        return Conv.asShort(ScriptUtils.toValue(value));
    }

    public int asJInt(Value value) {
        return Conv.asInteger(ScriptUtils.toValue(value));
    }

    public long asJLong(Value value) {
        return Conv.asLong(ScriptUtils.toValue(value));
    }

    public Timestamp newJTimestamp() {
        return new Timestamp(System.currentTimeMillis());
    }

    public double asJDouble(Value value) {
        return Conv.asDouble(ScriptUtils.toValue(value));
    }

    public float asJFloat(Value value) {
        return Conv.asFloat(ScriptUtils.toValue(value));
    }

    public boolean asJBoolean(Value value) {
        return Conv.asBoolean(ScriptUtils.toValue(value));
    }

    public String asJString(Value value) {
        return Conv.asString(ScriptUtils.toValue(value));
    }

    public Date asJDate() {
        return new Date();
    }

    public Date asJTimestamp() {
        return new Timestamp(System.currentTimeMillis());
    }

    public DateTime asJodaTime() {
        return DateTime.now();
    }

    /**
     * 创建Java java.util.Date 对象,支持以下格式<br />
     * "yyyy-MM-dd HH:mm:ss" <br />
     * "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'" <br />
     * "EEE MMM dd HH:mm:ss zzz yyyy" <br />
     * "yyyy-MM-dd" <br />
     * "HH:mm:ss" <br />
     */
    @SneakyThrows
    public Date asJDate(Value value) {
        if (value == null || value.isNull()) {
            return new Date();
        }
        try {
            if (value.isString()) {
                return DateUtils.parseDate(value.asString(), Date_Patterns);
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

        Object valueRaw = ScriptUtils.toValue(value);
        if (valueRaw instanceof DateTime) {
            return ((DateTime) valueRaw).toDate();

        } else if (valueRaw instanceof Timestamp) {
            return new Date(((Timestamp) valueRaw).getTime());

        } else if (valueRaw instanceof TIMESTAMP) {
            return new Date(((TIMESTAMP) valueRaw).timestampValue().getTime());

        }
        return Conv.asDate(valueRaw);
    }

    /**
     * 创建Java java.util.Date 对象,支持以下格式<br />
     * "yyyy-MM-dd HH:mm:ss" <br />
     * "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'" <br />
     * "EEE MMM dd HH:mm:ss zzz yyyy" <br />
     * "yyyy-MM-dd" <br />
     * "HH:mm:ss" <br />
     */
    @SneakyThrows
    public Timestamp asJTimestamp(Value value) {
        if (value == null || value.isNull()) {
            return new Timestamp(System.currentTimeMillis());
        }
        try {
            if (value.isString()) {
                return new Timestamp(asJDate(value).getTime());
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

        Object valueRaw = ScriptUtils.toValue(value);
        if (valueRaw instanceof DateTime) {
            return new Timestamp(((DateTime) valueRaw).getMillis());

        } else if (valueRaw instanceof Timestamp) {
            return (Timestamp) valueRaw;

        } else if (valueRaw instanceof TIMESTAMP) {
            return ((TIMESTAMP) valueRaw).timestampValue();

        }
        return new Timestamp(asJDate(value).getTime());
    }

    /**
     * 创建Java java.util.Date 对象,支持以下格式<br />
     * "yyyy-MM-dd HH:mm:ss" <br />
     * "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'" <br />
     * "EEE MMM dd HH:mm:ss zzz yyyy" <br />
     * "yyyy-MM-dd" <br />
     * "HH:mm:ss" <br />
     */
    @SneakyThrows
    public DateTime asJodaTime(Value value) {
        if (value == null || value.isNull()) {
            return DateTime.now();
        }
        try {
            if (value.isString()) {
                return new DateTime(DateUtils.parseDate(value.asString(), Date_Patterns));
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

        Object valueRaw = ScriptUtils.toValue(value);
        if (valueRaw instanceof DateTime) {
            return (DateTime) valueRaw;

        } else if (valueRaw instanceof Timestamp) {
            return new DateTime(((Timestamp) valueRaw).getTime());

        } else if (valueRaw instanceof TIMESTAMP) {
            return new DateTime(((TIMESTAMP) valueRaw).timestampValue());

        }
        return new DateTime(asJDate(value));
    }

    public BigDecimal asJBigDecimal(Value value) {
        return Conv.asDecimal(ScriptUtils.toValue(value));
    }

    public BigInteger asJBigInteger(Value value) {
        return Conv.asDecimal(ScriptUtils.toValue(value)).toBigInteger();
    }

    public List<Object> asJList(Object... args) {
        if (args == null) {
            return null;
        }
        return Arrays.asList(args);
    }

    public List<Object> asJList(Object arg) {
        if (arg == null) {
            return null;
        }
        if (arg.getClass().isArray()) {
            Object[] array = (Object[]) arg;
            return Arrays.asList(array);
        } else {
            return Collections.singletonList(arg);
        }
    }

    public Set<Object> asJSet(Object... args) {
        if (args == null) {
            return null;
        }
        Set<Object> set = new HashSet<>(args.length);
        set.addAll(Arrays.asList(args));
        return set;
    }

    public Set<Object> asJSet(Object arg) {
        if (arg == null) {
            return null;
        }
        if (arg.getClass().isArray()) {
            Object[] array = (Object[]) arg;
            Set<Object> set = new HashSet<>(array.length);
            set.addAll(Arrays.asList(array));
            return set;
        } else {
            return Collections.singleton(arg);
        }
    }

    public abstract Map<Object, Object> asJMap(T arg);

    public Object fromJList(List<Object> values) {
        return values;
    }

    public Object fromJArray(Object... values) {
        return values;
    }

    public Object fromJMap(Map<String, Object> values) {
        return values;
    }

    public Object fromJDate(LocalDate date) {
        return date;
    }

    public Object fromJDate(Date date) {
        return date;
    }

    public Object fromJDate(java.sql.Date date) {
        return date;
    }

    public Object fromJDate(Duration duration) {
        return duration;
    }

    public Object fromJDate(Instant instant) {
        return instant;
    }

    public Object fromJDate(LocalTime localTime) {
        return localTime;
    }

    public Object fromJDate(ZoneId zoneId) {
        return zoneId;
    }

    /**
     * 获取某个模块的类名
     */
    public String getClassName(Value moduleInstance) {
        return moduleInstance.getMetaObject().getMember("className").asString();
    }

//    public Value toEntity(Value entityConstruct, Map map) {
//        Value prototype = entityConstruct.getMember("prototype");
//        Value cols = prototype.getMember("__cols");
//        Value value = entityConstruct.newInstance();
//
//        for (String propName : cols.getMemberKeys()) {
//            cols.getMember(propName);
//            String type = cols.getMember(propName).getMember("type").asString();
//            Object v = null;
//            switch (type) {
//                case "String":
//                    v = Conv.asString(map.get(propName));
//                    break;
//                case "JodaTime":
//                    if (map.get(propName) != null) {
//                        String propValue = String.valueOf(map.get(propName));
//                        v = new DateTime().withMillis(new Long(propValue));
//                    }
//                    break;
//                case "BigDecimal":
//                    v = Conv.asDecimal(map.get(propName));
//                    break;
//                case "Double":
//                    v = Conv.asDouble(map.get(propName));
//                    break;
//                case "Boolean":
//                    v = Conv.asBoolean(map.get(propName));
//                    break;
//                case "Integer":
//                    v = Conv.asInteger(map.get(propName));
//                    break;
//                case "Long":
//                    v = Conv.asLong(map.get(propName));
//                    break;
//                default:
//                    v = map.get(propName);
//            }
//
//            value.putMember(propName, v);
//        }
//
//        return value;
//    }

}
